package edu.kit.ksri.db.mturk.plugins.correctness;

import java.util.List;

import org.apache.log4j.Logger;

import com.amazonaws.mturk.requester.Assignment;

import edu.kit.ksri.db.mturk.connectionhandlers.HITManager;
import edu.kit.ksri.db.mturk.connectionhandlers.SimHITManager;
import edu.kit.ksri.db.mturk.plugins.correctness.answers.AbstractAssignmentAnswer;
import edu.kit.ksri.db.mturk.plugins.correctness.answers.AssignmentAnswerFactory;
import edu.kit.ksri.db.mturk.standaloneutils.OfflinePoolManager.Worker;
import edu.kit.ksri.db.mturk.utils.messaging.Result;
import edu.kit.ksri.db.mturk.utils.messaging.ResultOutputCollector;
import edu.kit.ksri.db.mturk.var.ConnectionMode;
import edu.kit.ksri.db.mturk.var.Icd;
import edu.kit.ksri.db.mturk.var.StaticInformation;
import edu.kit.ksri.db.mturk.workerpools.AbstractWorker;
import edu.kit.ksri.db.mturk.workerpools.CSP1Capable;
import edu.kit.ksri.db.mturk.workerpools.ICDWorker;
import edu.kit.ksri.db.mturk.workerpools.PoolManager;
import edu.kit.ksri.db.mturk.workerpools.SimWorker;
import edu.kit.ksri.db.mturk.workerpools.StandardWorker;

/**
 * Copyright (c) 2010-2011 Karlsruhe Institute of Technology (KIT), Germany
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 * 
 * --------------------------------------------------------------------------
 * 
 * Software: CSP/WMV tool for dynamic quality management of microtasks
 * http://www.ksri.kit.edu/Upload/Users/PeopleClouds/HCOMP2011_An_extendable_toolkit_for_managing_quality_of_human-based_eServices.pdf
 * 
 * Organization: Karlsruhe Institute of Technology (KIT), Germany
 * http://www.kit.edu
 * 
 * Project: PeopleClouds project of the Karlsruhe Service Research Institute(KSRI)
 * http://www.ksri.kit.edu/Default.aspx?PageId=818
 * 
 * Contributing authors: David Bermbach, Sandra Rath, Pascal Wichmann, Robert Kern
 */
public abstract class AbstractCSP1AndWMVImplementation extends
		AbstractQualityControlImplementation {

	private static Logger basiclog = Logger
			.getLogger("basic." + AbstractCSP1AndWMVImplementation.class);

	@Override
	protected void onMessage(Result result) {
		// FIXME can we cope with more than one assignment within the same
		// result message?
		for (Assignment a : result.getAssignments()) {
			initCSP1(a);
		}

	}

	/**
	 * decides based on the specific worker statistics whether an assignment
	 * needs to be inspected or not. if yes the inspection is triggered.
	 * 
	 * @param assignment
	 */
	private void initCSP1(Assignment assignment) {
		basiclog.info("CSP1 initiated for HIT " + assignment.getHITId() + ".");
		AbstractWorker w = PoolManager.getInstance().getPoolForName("ACTIVE")
				.getWorkerForID(assignment.getWorkerId());
		if (w == null) {
			// worker has been moved to another pool in the mean time, check
			// whether we can find him in blocked or pending
			w = PoolManager.getInstance().getPoolForName("PENDING")
					.getWorkerForID(assignment.getWorkerId());
			if (w == null)
				w = PoolManager.getInstance().getPoolForName("BLOCKED")
						.getWorkerForID(assignment.getWorkerId());
			if (w == null) {
				// If the worker ID is also not found in another pool,
				// a new worker is created with an average correctness level
				// (this is for demo purposes only! For research experiments a qualification test should
				// always be used so you will never run into this code!)
				basiclog.info("WARNING: New worker with average correctness level was assumed.");
				if(StaticInformation.scenario instanceof Icd){
					// Assume fixed correctness level of 75 percent for ICD workers.
					w = new ICDWorker(assignment.getWorkerId(),75);
				}
				else if (StaticInformation.scenario.getPrefix().equals("AddressResearch")){
					// Assume fixed correctness level of 80 percent for OCR and poker workers.
					//w = new StandardWorker(assignment.getWorkerId(),10,8,2);
					w = new SimWorker(assignment.getWorkerId(),10,9,1);
					basiclog.debug("Worker is a SimWorker");
				}
				else{
					// Assume fixed correctness level of 80 percent for OCR and poker workers.
					//w = new StandardWorker(assignment.getWorkerId(),10,8,2);
					w = new StandardWorker(assignment.getWorkerId(),10,9,1);
				}

				PoolManager.getInstance().getPoolForName("ACTIVE").addWorker(w);				
			}
		}
		// Retrieve a result for the HIT ID
		Result r = ResultOutputCollector.getInstance().remove(
				assignment.getHITId());
		
		//basiclog.debug("Retrieve Result " + r.getHitId());
		boolean isFirstAssignmentForHIT = false;
		if (r != null)
			// Append assignment to existing result
			r = Result.mergeResults(r, new Result(assignment.getHITId(),
					assignment));
		else {
			// Create new result
			isFirstAssignmentForHIT = true;
			r = new Result(assignment.getHITId(), assignment);
		}
		// Store the complete result
		r.setWorkerRating(assignment.getWorkerId(), w.getCorrectnessLevel());
		basiclog.debug("Setting submit time rating of worker "+w.getWorkerId()+": " + w.getCorrectnessLevel()+"; result for HIT: " + assignment.getHITId());
		ResultOutputCollector.getInstance().put(assignment.getHITId(), r);

		if ( isFirstAssignmentForHIT ) {
			// if this is the first assignment of the result
			basiclog.info("Worker " + w.getWorkerId() ); 
			basiclog.info("Inspection of worker required? " + ((CSP1Capable)w).isMustBeInspectedForCSP1() );
			
			if (w instanceof CSP1Capable && ((CSP1Capable)w).isMustBeInspectedForCSP1() ) {
				// if inspection is required for the worker according to the CSP1
				// extend Hit and wait for another result, so that we have at least
				// two results for the WMV
				basiclog.info("Extending HIT " + r.getHitId() + ".");
				
				boolean success = false;
				
				if (StaticInformation.scenario.getPrefix() == "AddressResearch" && StaticInformation.scenario.getConnectionMode() == ConnectionMode.SIM){
					r.setCurrentPhi(w.getCorrectnessLevel()/100.0);
					success = ((SimHITManager)(HITManager.getInstance())).extendHit(r);
				}else{
					success = HITManager.getInstance().extendHit(r.getHitId());
				}
				
				if (success)
					basiclog.info("HIT " + r.getHitId() + " extended.");
				
				List<Assignment> assignments = r.getAssignments();
				String answers = "";
				for(Assignment a : assignments){
					answers = answers + AbstractAssignmentAnswer.parseAnswer(a.getAnswer()) + ", ";
				}
				basiclog.info("Answer was: " + answers);
				
				
				int retryIn = 0;
				while (!success) {
					retryIn++;
					// retry
					if (StaticInformation.scenario.getPrefix() == "AddressResearch" && StaticInformation.scenario.getConnectionMode() == ConnectionMode.SIM){
						r.setCurrentPhi(w.getCorrectnessLevel()/100.0);
						success = ((SimHITManager)(HITManager.getInstance())).extendHit(r);
					}else{
						success = HITManager.getInstance().extendHit(r.getHitId());
					}
					if (success) {
						basiclog.info("HIT " + r.getHitId() + " extended.");
						break;
					}
					basiclog.warn("Error while extending Hit " + r.getHitId()
							+ ". Retrying in " + retryIn + " seconds...");
					try {
						Thread.sleep(1000 * retryIn);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				ResultOutputCollector.getInstance().addRequestedAssignments(1);
			} else {
				// if inspection is not required, the WMV is not initiated: 
				// finalize result and update worker statistics
				basiclog.info("No inspection required for worker " + w.getWorkerId()
						+ " and assignment " + assignment.getAssignmentId());
				
				List<Assignment> assignments = r.getAssignments();
				String answers = "";
				for(Assignment a : assignments){
					answers = answers + AbstractAssignmentAnswer.parseAnswer(a.getAnswer()) + ", ";
				}
				basiclog.info("Answer was: " + answers);
				
				r.setCorrectSolution(AssignmentAnswerFactory
						.createInstance(assignment.getAnswer()));
				r.setFinalResult(true);
				r.setPhiCorrect(-1);//show that it was not inspected
				((CSP1Capable)w).addAssignmentForBinomialCSP1(false, true);
			} 
		} else {
			// if there is more than one assignment in the result, the 
			// process is already in inspection mode and the WMV needs to be initiated
			initWMV(assignment.getHITId());
		}
	}

	/**
	 * weighted majority vote: inspects the specified results and updates the
	 * correspondent worker's fields by invoking {@link Worker}
	 * .addAssignmentForBinomialCSP1
	 * 
	 * @param hitID
	 *            the id of the assignments which shall be analyzed
	 */
	protected abstract void initWMV(String hitID);

}
